package indi.zhifa.expshare.class001.chemicalmatch.service.impl;

import com.alibaba.excel.EasyExcel;
import indi.zhifa.expshare.class001.chemicalmatch.dao.INickNameRepository;
import indi.zhifa.expshare.class001.chemicalmatch.dao.IProductTypeRepository;
import indi.zhifa.expshare.class001.chemicalmatch.entity.dto.NickNameInfoRequest;
import indi.zhifa.expshare.class001.chemicalmatch.entity.dto.excel.ChemicalNickNameExcelData;
import indi.zhifa.expshare.class001.chemicalmatch.entity.dto.NickNameDto;
import indi.zhifa.expshare.class001.chemicalmatch.entity.dto.NickNameFindRequest;
import indi.zhifa.expshare.class001.chemicalmatch.entity.dto.NickNamePageRequest;
import indi.zhifa.expshare.class001.chemicalmatch.entity.po.NickNameMongoData;
import indi.zhifa.expshare.class001.chemicalmatch.entity.po.ProductTypeMongoData;
import indi.zhifa.expshare.class001.chemicalmatch.entity.vo.ProductTypeVo;
import indi.zhifa.expshare.class001.chemicalmatch.exel.ChemicalNickNameListener;
import indi.zhifa.expshare.class001.chemicalmatch.service.INickNameService;
import indi.zhifa.expshare.class001.chemicalmatch.util.openai.embding.OpenAiEmbedUtil;
import indi.zhifa.expshare.class001.chemicalmatch.util.redis.INickRedisUtil;
import indi.zhifa.engine.core.common.entity.web.ServiceException;
import indi.zhifa.engine.core.common.utils.DtoEntityUtil;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import redis.clients.jedis.UnifiedJedis;

import java.io.IOException;
import java.util.*;

@Slf4j
@RequiredArgsConstructor
@Component
public class NickNameServiceImpl implements INickNameService {

    final INickNameRepository mNickNameRepository;
    final IProductTypeRepository mProductTypeRepository;
    final OpenAiEmbedUtil mOpenAiEmbedUtil;
    final INickRedisUtil mNickRedisUtil;

    UnifiedJedis mJedis;
    static final String NICK_NAME_INDEX_NAME = "idx:nick_name";
    static final String NICK_NAME_KEY_PREFIX = "nick:";

    @PostConstruct
    public void init(){

    }


    @Override
    public NickNameMongoData info(String pId) {
        NickNameMongoData nickNameMongoData = mNickNameRepository.findById(pId).orElse(null);
        if(nickNameMongoData != null){
            return nickNameMongoData;
        }
        throw new ServiceException("没有找到Id为"+pId+"昵称数据");
    }

    @Override
    public NickNameMongoData info(NickNameInfoRequest pNickNameInfoRequest) {
        pNickNameInfoRequest.testAndInit();
        NickNameMongoData nickNameMongoData = mNickNameRepository.findByModuleAndSubModuleAndName(
                pNickNameInfoRequest.getModule(),
                pNickNameInfoRequest.getSubModule(),pNickNameInfoRequest.getName());
        if(nickNameMongoData != null){
            return nickNameMongoData;
        }
        throw new ServiceException("没有找到module为"+pNickNameInfoRequest.getModule()+
                "，subModule为"+pNickNameInfoRequest.getSubModule()+
                "，name为"+pNickNameInfoRequest.getName()+"的昵称数据");
    }

    final MongoTemplate mMongoTemplate;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importChemicalNickNameFromExcel(MultipartFile pFileRequest) throws IOException {
        ChemicalNickNameListener chemicalNickNameListener = new ChemicalNickNameListener(mMongoTemplate,mNickNameRepository,mNickRedisUtil);
        EasyExcel.read(pFileRequest.getInputStream(), ChemicalNickNameExcelData.class, chemicalNickNameListener)
                .sheet("化学昵称")
                .doRead();
    }

    protected List<String> addNickName(NickNameMongoData pNickNameEntity, List<String> pNickNames){
        List<String> nickNames = pNickNameEntity.getNickNames();
        Set<String> nickNameSet = new HashSet<>(nickNames);
        List<String> addNickName = new ArrayList<>();

        for(String nickName : pNickNames){
            nickName = nickName.trim();
            if(!nickNameSet.contains(nickName)){
                nickNames.add(nickName);
                addNickName.add(nickName);
            }
        }
        return addNickName;
    }
    @Override
    public void addNickName(String pId, List<String> pNickNames) {
        NickNameMongoData nickNameMongoData = mNickNameRepository.findById(pId).orElse(null);
        if(null == nickNameMongoData){
            throw new ServiceException("没有找到id为"+pId+"的昵称数据");
        }
        List<String> addNickNames = addNickName(nickNameMongoData,pNickNames);
        if(addNickNames.size()>0){
            mNickNameRepository.save(nickNameMongoData);
            mNickRedisUtil.addNickName(nickNameMongoData.getModule().name(),
                    nickNameMongoData.getSubModule(),
                    nickNameMongoData.getName(),
                    addNickNames);
        }
        mNickRedisUtil.addNickName(nickNameMongoData.getModule().name(),
                nickNameMongoData.getSubModule(),
                nickNameMongoData.getName(),
                pNickNames);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addNickName(NickNameDto pNickNameDto) {
        pNickNameDto.testAndInit();

        NickNameMongoData nickNameMongoData = mNickNameRepository.findByModuleAndSubModuleAndName(pNickNameDto.getModule(),pNickNameDto.getSubModule(),pNickNameDto.getName());
        if(nickNameMongoData == null){
            nickNameMongoData = DtoEntityUtil.trans(pNickNameDto, NickNameMongoData.class);
            nickNameMongoData.addNickName(pNickNameDto.getName());
            if("product".equals(nickNameMongoData.getModule())){
                if(!mProductTypeRepository.existsByProductType(nickNameMongoData.getSubModule())){
                    ProductTypeMongoData productTypeMongoData = new ProductTypeMongoData();
                    productTypeMongoData.setProductType(nickNameMongoData.getSubModule());
                    mProductTypeRepository.save(productTypeMongoData);
                }
            }
            mNickNameRepository.insert(nickNameMongoData);
            mNickRedisUtil.addNickName(nickNameMongoData.getModule().name(),
                    nickNameMongoData.getSubModule(),
                    nickNameMongoData.getName(),
                    nickNameMongoData.getNickNames());
        }else{
            List<String> addNickNames = addNickName(nickNameMongoData, pNickNameDto.getNickNames());
            if(addNickNames.size()>0){
                mNickNameRepository.save(nickNameMongoData);
                mNickRedisUtil.addNickName(nickNameMongoData.getModule().name(),
                        nickNameMongoData.getSubModule(),
                        nickNameMongoData.getName(),
                        addNickNames);
            }
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(String pId,String pNickName) {
        NickNameMongoData nickNameMongoData = mNickNameRepository.findById(pId).orElse(null);
        if(null == nickNameMongoData){
            throw new ServiceException("没有找到id为"+pId+"的昵称数据");
        }
        if(StringUtils.hasLength(pNickName)){
            nickNameMongoData.getNickNames().remove(pNickName);
            mNickNameRepository.save(nickNameMongoData);
        }else{
            mNickNameRepository.deleteById(pId);
            if(nickNameMongoData.getModule().equals("product")){
                // 没有数据了，就把类型删了
                int cnt = mNickNameRepository.countBySubModule(nickNameMongoData.getSubModule());
                if(cnt == 0){
                    // 先查再删，防止锁表
                    ProductTypeMongoData deleteData = mProductTypeRepository.findByProductType(nickNameMongoData.getSubModule());
                    if(null != deleteData){
                        mProductTypeRepository.deleteById(deleteData.getId());
                    }
                }
            }
        }

    }

    @Override
    public NickNameMongoData findNickname(NickNameFindRequest pNickNameFindRequest) {
        pNickNameFindRequest.testAndInit();

        // 先从redis里找
        String name = mNickRedisUtil.queryNickName(
                pNickNameFindRequest.getModule().name(),
                pNickNameFindRequest.getSubModule(),
                pNickNameFindRequest.getNickName());
        NickNameMongoData rtn = null;
        if(null != name){
            rtn = mNickNameRepository.findByModuleAndSubModuleAndName(pNickNameFindRequest.getModule(),
                    pNickNameFindRequest.getSubModule(),name);
        }else{
            rtn =mNickNameRepository.findByModuleAndSubModuleAndNickNamesContains(pNickNameFindRequest.getModule(),
                    pNickNameFindRequest.getSubModule(),pNickNameFindRequest.getNickName());
        }
        if(rtn == null){
            // todo 使用向量搜索，找一个最近似的
            throw new ServiceException("该昵称不存在");
        }
        return rtn;
    }

    @Override
    public List<ProductTypeVo> getProductTypeList() {

        List<ProductTypeVo> rtn = new ArrayList<>();
        // 查询所有的productType
        List<ProductTypeMongoData> productTypeMongoDataList = mProductTypeRepository.findAll();

        for(ProductTypeMongoData data : productTypeMongoDataList){
            List<String> nameList = mNickNameRepository.findDistinctNameBySubModule(data.getProductType());
            ProductTypeVo vo = new ProductTypeVo();
            vo.setId(data.getId());
            vo.setTypeName(data.getProductType());
            vo.setNames(nameList);
            vo.setCount(nameList.size());
            rtn.add(vo);
        }
        return rtn;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void clear() {
        // 清除缓存
        List<NickNameMongoData> nickNameMongoDataList = mNickNameRepository.findAll();
        for(NickNameMongoData data : nickNameMongoDataList){
            mNickRedisUtil.deleteNickName(data.getModule().name(),
                    data.getSubModule(),
                    data.getName(),
                    data.getNickNames());
        }

        mMongoTemplate.remove(new Query(),"nick_name");
        mMongoTemplate.remove(new Query(),"product_type");
    }

    @Override
    public Map<String, Page<NickNameMongoData>> page(List<NickNamePageRequest> pRequestList) {
        Map<String, Page<NickNameMongoData>> rtn = new HashMap<>();
        for(NickNamePageRequest request : pRequestList){
            Query query = new Query();
            if("chemical".equals(request.getProductType())){
                query.addCriteria(Criteria.where("module").is("chemical"));
            }else{
                query.addCriteria(Criteria.where("module").is("product"));
                query.addCriteria(Criteria.where("subModule").is(request.getProductType()));
            }
            long total = mMongoTemplate.count(query, NickNameMongoData.class,  "nick_name");
            List<NickNameMongoData> data = null;
            if(total > 0 && total > (request.getCurrent()-1) * request.getSize()){
                long skip = (long) (request.getCurrent()-1) * request.getSize();
                query.skip(skip).limit(request.getSize());
                data = mMongoTemplate.find(query, NickNameMongoData.class,  "nick_name");
            }
            if(null == data){
                data = new ArrayList<>();
            }
            Page<NickNameMongoData> page = new PageImpl<>(data, PageRequest.of(request.getCurrent(), request.getSize()), total);
            rtn.put(request.getProductType(), page);
        }
        return rtn;
    }
}
